import Vue from 'vue'
import request from './request'
import { cloneDeep } from 'lodash'

// 不要覆盖继承的属性

// 可以进行数据的请求 也可以保存数据的状态
// Operation 操作类(有状态管理) AxiosProxy代理类(无状态管理)
/*
操作类

Store模型有两部分 
  1.this.view 用于保存请求来的数据并为table内容渲染提供支持
  2.具体的请求方法 用于获取table渲染内容的数据 --> 通过AxiosProxy进行请求 === util工具
但是 通过AxiosProxy进行请求数据的方式 并不只有一种 就是直接请求数据并且将数据交给this.view --> 所以需要一个操作类 Operation 来协助处理 AxiosProxy

table组件不同的请求需求 --> 交给Operation --> 控制AxiosProxy --> 得到结果
*/
class Operation {
  constructor(store) {
    //
    this.store = store
    // store中的 this.proxy
    this.proxy = store.proxy
    //  this.view
    // 操作类的状态
    this.status = 0
  }

  // 执行函数 需要执行一次这个函数 才会有之后的操作 页面回滚 重新请求 等
  async execute(fillter, filterParams) {
    // 状态不为0 则不执行
    if (this.status !== 0) return

    this.fillter = fillter
    this.filterParams = filterParams

    // 通过操作类来取消上一次请求
    this.pre?.abort()

    // 当前的操作类应该是上一次的操作类 因为上一次的操作类已经执行过了
    this.pre = this.store.lastOperation

    if (this.store.lastOperation) {
      this.store.lastOperation.last = this
    }

    // 这是保存最后一次操作 无论操作是什么结果 都被保存了
    this.store.lastOperation = this

    this.store.view.status = 1

    // 执行传入的store方法
    await this.proxy.load(this)

    if (this.status === 4) {
      // 操作取消 状态回滚
      if (!this.last) {
        this.store.view.status = 2
        this.store.lastOperation.reset()
      }
      return
    }

    this.store.view.status = this.status

    if (this.status === 2) {
      // 这是保存请求成功之后的所有状态 比如store中的view 仅保存了成功了请求结果
      this.store.lastSuccessOperation = this
      // 对store进行修正
      this.reset()
    }
  }

  // 重新执行
  refresh() {
    const operation = new Operation(this.store)
    operation.execute(this.fillter, this.filterParams)
  }
  // 状态重置
  reset() {
    Object.assign(this.store.view, cloneDeep(this.view))
  }
  // 取消操作
  abort() {
    console.log('op')

    this.cancel.abort()
  }
}

// 自选方式 如果需要可以移植 (模型)
export class Store3 {
  constructor(data = []) {
    // 分页器是独立组件应有自己的数据状态
    this.filterParams = {
      page: 1,
      pageSize: 10
    }

    // this.view是table组件的数据状态
    this.view = Vue.observable({
      data,
      page: 1,
      pageSize: 10,
      total: 0,
      status: 0,
      loading: false
    })

    // 操作类?
    // 上一次成功的数据

    this.lastSuccessOperation

    this.lastOperation

    this._isNeedLoad = false

    // 在created这个时机执行 this.store.load 这个函数 由于生命周期的问题同步任务先执行 微任务并未执行 -> 导致this.proxy.load这个函数为空
    // 但是在visible这个时机 同步任务与微任务已经执行完毕 在执行this.proxy.load这个函数就不会出错
    // 主要的问题就是同步异步问题导致的this.proxy.load为空的情况
    Promise.resolve().then(() => {
      this.proxy = createProxyInstance(this)
      // 同步执行代码中并未发送请求所以要在微任务中执行请求
      if (this._isNeedLoad) {
        // 同步修改
        const operation = new Operation(this)
        operation.execute(this.filter, this.filterParams)
      }
    })
  }

  // table数据请求
  async loadData(params) {
    // 竟态问题 1.get请求直接取消上一个请求的结果(或者是忽略) 2.post等待上一个请求结果结束 再发送新的请求结果(或者是忽略)

    // 所以我要在这个地方进行判断 如果有this.proxy.load这个函数是否存在 存在就是visible的加载时机 直接执行即可
    // 反之就是created的加载时机创建一个标志 在微任务执行时帮助执行this.proxy.load这个函数获取数据
    this.filter = params || {}
    if (this.proxy.load) {
      // 创建操作类并传入this 以便于操作类 执行数据请求
      // 在这里面传入this === 传入this.view 传入this.proxy 等于传入所有东西
      const operation = new Operation(this)
      // 执行数据请求
      await operation.execute(this.filter, this.filterParams)
    } else {
      this._isNeedLoad = true
    }
  }
  // 分页器请求
  async loadPage(params) {
    this.filterParams.page = params.page || 1
    this.filterParams.pageSize = params.pageSize || 10
    this.loadData()
  }
  // 刷新请求 1.请求最后一次操作 2.复制最后一次成功的操作
  async refresh(flag) {
    // 重新执行上一次的操作
    if (flag) {
      this.lastOperation.refresh()
    } else {
      // 复制上一次执行的操作
      this.lastSuccessOperation.reset()
    }
  }
  // 重置请求
  reset() {
    this.lastSuccessOperation.reset()
  }
  // 取消请求
  abort() {
    console.log('s3')

    this.lastOperation.abort()
  }
  get status() {
    return this.view.status
  }
}
// 正常请求 和  但是处理时应由具体的模型确定(选用)
class AxiosProxy {
  constructor(config) {
    this.url = config.url || ''
  }

  // 通过operation类执行数据请求
  async load(operation) {
    // 解构操作类的内容
    const { filter, filterParams } = operation

    // 更该操作类的数据请求状态
    operation.status = 1

    operation.pre?.abort()

    // 为操作类设置取消请求的方式
    const controller = new AbortController()
    const signal = controller.signal

    operation.cancel = controller

    try {
      const { data } = await request.get(this.url, {
        params: {
          // 数据序列化
          ...this.setLoadPage(filterParams),
          ...filter
        },
        signal
      })

      if (this.getSuccess(data)) {
        // 修改状态码
        operation.status = 2
        // 修改数据状态
        operation.view = {
          data: this.getData(data),
          page: this.getPage(data),
          pageSize: this.getPageSize(data),
          total: this.getTotal(data)
        }
      }
    } catch (error) {
      if (error.code === 'ERR_CANCELED') {
        operation.status = 4
      } else {
        operation.status = 3
      }
      // 根据axios的不同错误信息进行处理
      // if (ErrorCodeHandle[error.code]) {
      //   ErrorCodeHandle[error.code](this)
      // } else {
      //   console.log(error)
      // }
    }
  }

  // 复用axios成功解析逻辑
  getSuccess(res) {
    return res?.status === 200
  }
  getData(res) {
    return res?.data?.data || []
  }
  getPage(res) {
    return res?.data?.page || 1
  }
  getPageSize(res) {
    return res?.data?.pageSize || 10
  }
  getTotal(res) {
    return res?.data?.total || 0
  }
  setLoadPage(filterParams) {
    return {
      P: filterParams.page,
      PS: filterParams.pageSize
    }
  }
}

// 缓存请求
export class CacheProxy extends AxiosProxy {
  async load(params) {
    // 上一个请求可以取消 并且 请求为完成
    if (this.controller && this.view.status === 1) {
      this.controller.abort()
    }
    this.controller = new AbortController()
    const signal = this.controller.signal

    this.view.status = 1

    // 标志位代表是否需要重新请求
    let isInit = false
    // 如果存在申请过的数据就直接返回
    if (this.loaclData) {
      Object.assign(this.view, this.loaclData)
    } else {
      isInit = true
    }
    try {
      if (isInit) {
        this.view.status = 1
      }
      // 新的请求会放在缓存中等待合并
      const { data } = await request.get(this.url, {
        ...params,
        signal
      })

      if (this.getSuccess(data)) {
        if (isInit) {
          this.view.status = 2
        }
        this.loaclData = {
          data: this.getData(data),
          page: this.getPage(data),
          pageSize: this.getPageSize(data),
          total: this.getTotal(data)
        }
        if (isInit) {
          Object.assign(this.view, this.loaclData)
        }
      }
    } catch (error) {
      if (isInit) {
        this.view.status = 3
      }
      console.log(error)
    }
  }
}

// 数据分页请求 前端处理
export class AxiosAllProxy extends AxiosProxy {
  getSuccess(res = {}, filterParams) {
    return res?.status === 200
  }
  getData(res = [], filterParams) {
    const { page, pageSize } = filterParams
    return res.slice((page - 1) * pageSize, page * pageSize) || []
  }
  getPage(res = {}, filterParams) {
    return filterParams.page || 1
  }
  getPageSize(res = {}, filterParams) {
    return filterParams.pageSize || 10
  }
  getTotal(res = {}, filterParams) {
    return res?.data?.total || 0
  }
  getAll(res) {
    return res
  }
  async load(operation) {
    // 解构操作类的内容
    const { filter, filterParams } = operation

    // 更该操作类的数据请求状态
    operation.status = 1
    // 如果有已经有请求结果了 就使用请求结果 没有请求结果就发送新的请求
    if (operation.pre) {
      //
      const allData = operation.pre.view.all
      operation.view = {
        all: allData,
        data: this.getData(allData, filterParams),
        page: this.getPage(allData, filterParams),
        pageSize: this.getPageSize(allData, filterParams),
        total: this.getTotal(allData, filterParams)
      }
      operation.status = 2
    } else {
      const controller = new AbortController()
      const signal = controller.signal

      // 为操作类设置取消请求的方式
      operation.cancel = controller

      try {
        const { data } = await request.get(this.url, {
          params: {
            // 数据序列化
            ...filter
          },
          signal
        })

        if (this.getSuccess(data)) {
          // 修改状态码
          operation.status = 2
          const allData = this.getAll(data)
          // 修改数据状态
          operation.view = {
            all: allData,
            data: this.getData(allData, filterParams),
            page: this.getPage(allData, filterParams),
            pageSize: this.getPageSize(allData, filterParams),
            total: this.getTotal(allData, filterParams)
          }
        }
      } catch (error) {
        if (error.code === 'ERR_CANCELED') {
          operation.status = 4
        } else {
          operation.status = 3
        }
        // 根据axios的不同错误信息进行处理
        // if (ErrorCodeHandle[error.code]) {
        //   ErrorCodeHandle[error.code](this)
        // } else {
        //   console.log(error)
        // }
      }
    }
  }
}

export class Model {
  constructor(data = {}) {
    this.view = Vue.observable({
      data
    })
  }

  // 查指定数据
  async findData(id) {
    this.status = 1
    try {
      const { data } = await request.get(this.proxy.action.findDataUrl)
      if (data.status === 200) {
        this.status = 2
        this.data = data.data
      }
    } catch (e) {
      this.status = 3
      console.log(e)
    }
  }
  // 新增数据
  async editorData(params) {
    this.status = 1
    try {
      const { data } = await request.get(this.proxy.action.findDataUrl, { params })
      if (data.status === 200) {
        this.status = 2
        this.data = data.data
      }
    } catch (e) {
      this.status = 3
      console.log(e)
    }
  }

  get data() {
    return this.view.data
  }
  set data(newVal) {
    this.view.data = newVal
  }
}

// 策略模式 代替 if 可以抽离
const ErrorCodeHandle = {
  // 请求错误
  ERR_BAD_REQUEST: intance => {
    Object.assign(intance.view, {
      data: intance.getData(),
      page: intance.getPage(),
      pageSize: intance.getPageSize(),
      total: intance.getTotal()
    })
    intance.view.status = 3
  },
  // 取消请求
  ERR_CANCELED: intance => {
    // 加载状态应该改为请求中
    intance.view.status = 1
  }
}

// 工厂模式
function createProxyInstance(store) {
  const type = store.proxy.type
  if (type === 'axiosProxy') {
    return new AxiosProxy(store.proxy, store)
  } else if (type === 'cacheProxy') {
    return new CacheProxy(store.proxy, store)
  } else if (type === 'axiosAllProxy') {
    return new AxiosAllProxy(store.proxy, store)
  }
}
